package utilities;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

public class ByteConstructor {
	//private ArrayList<Byte> bytes;
	private byte actualByte;
	private byte actualPosition;
	private FileOutputStream fos;
	
	private int bytesWritten;
	private int lastPrintStatistics;
	private int lastPrintActualPosition;
	public ByteConstructor () {
		//bytes = new ArrayList<Byte>();
		actualByte = 0;
		actualPosition = 0;
		bytesWritten = 0;
		lastPrintStatistics = 0;
		lastPrintActualPosition = 0;
	}
	
	public ByteConstructor (FileOutputStream fos) {
		this.fos = fos;
		actualByte = 0;
		actualPosition = 0;
		bytesWritten = 0;
		lastPrintStatistics = 0;
		lastPrintActualPosition = 0;
	}
	
	public byte[] putOne () throws IOException{
		actualByte = (byte)((actualByte<<1) + 1);
		actualPosition ++;
		if (actualPosition == 8){
			actualPosition = 0;
			byte[] ret = new byte[1];
			ret[0] = new Byte(actualByte);
			actualByte = 0;
			if (fos != null){
				fos.write(ret);
				bytesWritten ++;
			}
			return ret;
		}
		return null;
	}
	
	public byte[] putZero () throws IOException{
		actualByte = (byte)(actualByte<<1);
		actualPosition ++;
		if (actualPosition == 8){
			actualPosition = 0;
			byte[] ret = new byte[1];
			ret[0] = new Byte(actualByte);
			actualByte = 0;
			if (fos != null){
				fos.write(ret);
				bytesWritten ++;
			}
			return ret;
		}
		return null;
	}
	
	public byte[] putBit (boolean value) throws IOException{
		if (value == true){
			return putOne();
		}
		return putZero();
	}
	
	public byte[] putSequence (boolean[] values) throws IOException{
		ArrayList<Byte> ret = new ArrayList<Byte>();
		for (int i=0; i<values.length; i++){
			byte[] bytes = putBit(values[i]);
			if (bytes != null){
				ret.add(new Byte(bytes[0]));
			}
		}
		if (ret.size() > 0){
			byte[] toReturn = new byte[ret.size()];
			for (int i=0; i<ret.size(); i++){
				toReturn[i] = ret.get(i).byteValue();
			}
			return toReturn;
		}
		return null;
	}
	
	public byte[] putSequence (String values) throws IOException{
		ArrayList<Byte> ret = new ArrayList<Byte>();
		for (int i=0; i<values.length(); i++){
			boolean val = true;
			if (values.charAt(i) == '1'){
				val = true;
			} else {
				val = false;
			}
			byte[] bytes = putBit(val);
			if (bytes != null){
				ret.add(new Byte(bytes[0]));
			}
		}
		if (ret.size() > 0){
			byte[] toReturn = new byte[ret.size()];
			for (int i=0; i<ret.size(); i++){
				toReturn[i] = ret.get(i).byteValue();
			}
			return toReturn;
		}
		return null;
	}
	
	public byte[] putByte(Byte b) throws IOException{
		return putByte(b.byteValue());
	}
	
	public byte[] putByte(byte b) throws IOException{
		byte[] ret = null;
		for (int i = 7; i>=0; i--){
			int bit = (b >> i) & 1;
			byte[] byteArr = null;
			if (bit == 0){
				byteArr = putZero();
			} else if (bit == 1){
				byteArr = putOne();
			}
			if (byteArr != null){
				ret = byteArr;
			}
		}
		return ret;
	}
	
	public byte[] writeBytes(byte[] bytes) throws IOException{
		ArrayList<Byte> ret = new ArrayList<Byte>();
		if (bytes != null){
			for (int i = 0; i < bytes.length; i++){
				byte[] added = putByte(bytes[i]);
				if (added != null){
					for (int j=0; j<added.length; j++){
						ret.add(new Byte(added[j]));
					}
				}
			}
			if (ret.size() > 0){
				byte[] toReturn = new byte[ret.size()];
				for (int i=0; i<ret.size(); i++){
					toReturn[i] = ret.get(i).byteValue();
				}
				return toReturn;
			}
		}
		return null;
	}
	
	public byte[] writeBytes(Byte[] bytes) throws IOException{
		ArrayList<Byte> ret = new ArrayList<Byte>();
		if (bytes != null){
			for (int i = 0; i < bytes.length; i++){
				byte[] added = putByte(bytes[i]);
				if (added != null){
					for (int j=0; j<added.length; j++){
						ret.add(new Byte(added[j]));
					}
				}
			}
			if (ret.size() > 0){
				byte[] toReturn = new byte[ret.size()];
				for (int i=0; i<ret.size(); i++){
					toReturn[i] = ret.get(i).byteValue();
				}
				return toReturn;
			}
		}
		return null;
	}
	
	public byte[] flush() throws IOException{
		if (actualPosition == 0){
			return null;
		} else {
			actualByte = (byte)(actualByte<< (8-actualPosition));
			byte[] ret = new byte[1];
			ret[0] = new Byte(actualByte);
			actualByte = 0;
			actualPosition = 0;
			if (fos != null){
				fos.write(ret);
				bytesWritten ++;
			}
			return ret;
		}
	}
	public void printStatistics(){
		System.out.println("Bytes written to file: "+bytesWritten+" bits waiting to be written: "+actualPosition+" Total bits passed to read: "+(bytesWritten*8+actualPosition));
		System.out.println("Bytes written since last statistics: "+(bytesWritten-lastPrintStatistics)+" Total bits passed to read since last statistics: "+((bytesWritten-lastPrintStatistics)*8+actualPosition-lastPrintActualPosition));
		
		lastPrintStatistics = bytesWritten;
		lastPrintActualPosition = actualPosition;
	}
	
	
}
